// 一个简单的调用X11库实现的X11客户端示例

#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/keysym.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <getopt.h>
#include <time.h>       // 添加时间处理功能
#include <sys/select.h> // 添加select功能用于定时
#include <sys/time.h>   // 添加gettimeofday功能，获取毫秒级时间
#include <stdint.h>


// 自定义错误处理器
int handleXError(Display *display, XErrorEvent *error) {
    char errorText[256];
    XGetErrorText(display, error->error_code, errorText, sizeof(errorText));
    
    fprintf(stderr, "X11 Error:\n"
                    "  Request: %d (minor %d)\n"
                    "  Error Code: %d (%s)\n"
                    "  Resource ID: 0x%lx\n"
                    "  Serial: %lu\n",
            error->request_code,
            error->minor_code,
            error->error_code, errorText,
            error->resourceid,
            error->serial);
    return 0; // 返回值会被 Xlib 忽略
}

// 字节序检测
void detect_byte_order(Display *display) {
    // 检测X Server字节序
    int server_byte_order = ImageByteOrder(display);
    
    // 检测本地字节序
    int local_little_endian = 1;
    union { unsigned int i; unsigned char c[4]; } test = { 1 };
    local_little_endian = test.c[0];
    
    printf("=== Byte Order Detection Results ===\n");
    printf("X Server Byte Order: %s\n", 
           server_byte_order == LSBFirst ? "Little Endian (LSB First)" : "Big Endian (MSB First)");
    printf("Local Machine Byte Order: %s\n", 
           local_little_endian ? "Little Endian" : "Big Endian");
    
    if ((server_byte_order == LSBFirst) == local_little_endian) {
        printf("Byte order matches, no conversion needed\n");
    } else {
        printf("Byte order mismatch, byte conversion required\n");
    }
}


/**
 * Simple X11 GUI Program
 * Usage: ./x11_client_test [-d display]
 * Example: ./x11_client_test -d :0
 */

static void* create_test_image(int mode) {
    // 创建一个 100x100 的方块像素数据
    static uint32_t *frame_buffer;
    if(!frame_buffer) 
        frame_buffer = calloc(100 * 100, sizeof(uint32_t));
    if(!frame_buffer)
        return NULL;
    uint32_t color[] = { 0xFF0000, 0x00FF00, 0x0000FF }; //R G B
    for (int i = 0; i < 100 * 100; i++) {
        frame_buffer[i] = 0xff000000 | color[mode % 3];
    }
    return frame_buffer;
}

int main(int argc, char *argv[]) {
    Display *display;
    Window window;
    XEvent event;
    int screen;
    char *display_name = NULL;  // 默认连接到默认显示器
    int opt;
    char time_str[64];  // 用于存储时间字符串

    // 解析命令行参数
    while ((opt = getopt(argc, argv, "d:")) != -1) {
        switch (opt) {
            case 'd':
                display_name = optarg;
                break;
            default:
                fprintf(stderr, "Usage: %s [-d display]\n", argv[0]);
                exit(EXIT_FAILURE);
        }
    }

    // 连接到X服务器
    display = XOpenDisplay(display_name);
    if (display == NULL) {
        fprintf(stderr, "Cannot connect to X server '%s'\n", 
                display_name ? display_name : "default display");
        exit(EXIT_FAILURE);
    }

    // 注册错误处理器
    XSetErrorHandler(handleXError);    

    // 获取默认屏幕
    screen = DefaultScreen(display);
    if (screen < 0) {
        fprintf(stderr, "Cannot get default screen\n");
        XCloseDisplay(display);
        exit(EXIT_FAILURE);
    }
    printf("Connected to X server: %s, Screen: %d\n", 
           XDisplayName(display_name), screen);
    
    // 获取屏幕宽度、高度和颜色深度
    int width =  XDisplayWidth(display, screen);
    int height = XDisplayHeight(display, screen);
    int depth =  DefaultDepth(display, screen);
    printf("Screen size: %dx%d, Color depth: %d\n", width, height, depth);

    //获得root
    Window root = RootWindow(display, screen);
    printf("Root window ID: 0x%lx\n", root);

    // 创建一个简单的窗口
    window = XCreateSimpleWindow(display, 
                                RootWindow(display, screen),
                                100, 100,    // 初始位置
                                400, 300,    // 窗口大小
                                1,           // 边框宽度
                                BlackPixel(display, screen),  // 边框颜色
                                WhitePixel(display, screen)); // 背景颜色

    if(window == 0) {
        printf("Cannot create window\n");
        XCloseDisplay(display);
        exit(EXIT_FAILURE);
    }

    // 设置窗口属性
    XSetStandardProperties(display, window, 
                          "X11 Simple GUI", "X11",
                          None, argv, argc, NULL);


    // 获取默认的Visual
    Visual* vis = DefaultVisual(display,  screen);
    printf("Default Visual:\n");
    printf(" -ID: 0x%lx\n", vis->visualid);
    printf(" -depth: %d\n", DefaultDepth(display, screen));
    printf(" -class: %d\n", vis->class);
    printf(" -Visual bits per RGB: %d\n", vis->bits_per_rgb);
    printf(" -red mask: 0x%lx\n", vis->red_mask);
    printf(" -green mask: 0x%lx\n", vis->green_mask);
    printf(" -blue mask: 0x%lx\n", vis->blue_mask);
    printf(" -colormap size: %d\n", vis->map_entries);

    // 获取字节序
    int byte_order = ImageByteOrder(display);
    printf("Image byte order: %s\n", byte_order == LSBFirst ? "LSBFirst" : "MSBFirst");
    detect_byte_order(display);

    // 选择我们感兴趣的事件
    XSelectInput(display, window, 
                ExposureMask | KeyPressMask | ButtonPressMask | 
                StructureNotifyMask);

    // 显示窗口
    XMapWindow(display, window);
    
    // 等待窗口映射完成
    XEvent map_event;
    do {
        XNextEvent(display, &map_event);
    } while (map_event.type != MapNotify || map_event.xmap.window != window);
    
    XClearArea(display, window, 0, 0, 0, 0, True); //主动触发一次重绘
    
    XFlush(display); // 确保所有请求都发送到X服务器
    
    // 事件循环
    while (1) {
        // 获取X11连接的文件描述符
        int x11_fd = ConnectionNumber(display);
        fd_set in_fds;
        struct timeval tv;
        
        // 设置select的超时时间为1毫秒
        tv.tv_sec = 0;
        tv.tv_usec = 100000; // 1000微秒 = 1毫秒
        
        // 初始化文件描述符集
        FD_ZERO(&in_fds);
        FD_SET(x11_fd, &in_fds);
        
        // 等待X11事件或者超时
        int num_ready_fds = select(x11_fd + 1, &in_fds, NULL, NULL, &tv);
        
        // 获取当前时间（精确到毫秒）
        struct timeval tv_now;
        gettimeofday(&tv_now, NULL);
        
        // 获取标准日期时间部分
        time_t now_sec = tv_now.tv_sec;
        struct tm *tm_info = localtime(&now_sec);
        
        // 格式化时间字符串，包括毫秒
        int millisec = tv_now.tv_usec / 1000;
        snprintf(time_str, sizeof(time_str), 
                "%04d-%02d-%02d %02d:%02d:%02d.%03d",
                tm_info->tm_year + 1900, tm_info->tm_mon + 1, tm_info->tm_mday,
                tm_info->tm_hour, tm_info->tm_min, tm_info->tm_sec, millisec);
        
        // 如果有X11事件待处理
        if (FD_ISSET(x11_fd, &in_fds)) {
            XNextEvent(display, &event);
            
            switch (event.type) {
                case Expose:
                    // 处理重绘事件
                    if (event.xexpose.count == 0) {
                        // 绘制一些文本
                        XDrawString(display, window, 
                                   DefaultGC(display, screen),
                                   80, 200, 
                                   "Hello world!", 
                                   strlen("Hello world!")); //XDrawString 不支持中文                         
                    }
                    break;
                case KeyPress:
                    // 处理键盘事件，按ESC键退出
                    printf("Key event: keycode=%d\n", event.xkey.keycode);
                    if (XLookupKeysym(&event.xkey, 0) == XK_Escape) {
                        XCloseDisplay(display);
                        return 0;
                    }
                    break;
                case ButtonPress:
                    // 处理鼠标点击
                    printf("Mouse button %d click at (%d, %d)\n", 
                           event.xbutton.button, event.xbutton.x, event.xbutton.y);
                    break;
            }
        } else {
            // 如果没有X11事件，则绘制时钟
            // 清除特定区域（时钟区域）
            XClearArea(display, window, 50, 250, 300, 30, False);
            
            // 绘制时间
            XDrawString(display, window, 
                        DefaultGC(display, screen),
                        50, 270, 
                        time_str, 
                        strlen(time_str));

            static long last_sec = 0;
            long current_sec =  time(NULL);
            if (current_sec != last_sec) {
                void* pixels = create_test_image(current_sec);
                last_sec = current_sec;
                XImage *image = XCreateImage(display, vis, depth , ZPixmap,
                                                0, pixels, 100, 100, 32, 0);        
                XClearArea(display, window, 50, 50, 100, 100, False);
                XPutImage(display, window, DefaultGC(display, screen), image,
                            0, 0,  // 源坐标
                            50, 50, // 目标坐标
                            100, 100); // 宽高    
                image->data = NULL;
                XDestroyImage(image);
            }
            XFlush(display);
        }
    }

    // 关闭连接
    XCloseDisplay(display);
    return 0;
}
